/* ========================================
 *
 * Copyright YOUR COMPANY, THE YEAR
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF your company.
 *
 * ========================================
*/

#include "beachday.h"


// module definitions
uint32_t LatestReading = 0;
uint32_t MQ303Enabled = FALSE;

#define MQ303_ADC_CH_PU_VAL       270000  // pull-up value in Ohms

#define MQ303_WAIT_TO_INIT             0  //  0 tick wait on entry to ST_INIT
#define MQ303_WAIT_TO_HEAT_HV      MS(10) // 10 ticks @1KHz wait on entry to ST_HEAT_HV
#define MQ303_WAIT_TO_HEAT_LV   MS(12000) // 12 seconds @1KHz wait on entry to ST_HEAD_LV
#define MQ303_WAIT_TO_MAINT     MS(10000) // 10 seconds @1KHz wait on entry to ST_MAINT
#define MQ303_WAIT_TO_IDLE      MS(10000) // 10 seconds @1KHz wait on entry to ST_IDLE

// module prototypes
uint32_t ConvADCToBACPPM( uint32_t luv );

// module loop
uint32_t ServMQ303( uint32_t value )
{
#define MQ303_HEATER_DISABLE        do { Heat_switch_Write(GPIO_HIGH); Heat_2v2_Write(GPIO_LOW); } while (0)
#define MQ303_HEATER_MAKE_0p9V      do { Heat_switch_Write(GPIO_LOW); Heat_2v2_Write(GPIO_LOW); } while (0)
#define MQ303_HEATER_MAKE_2p2V      do { Heat_switch_Write(GPIO_LOW); Heat_2v2_Write(GPIO_HIGH); } while (0)
    
    static uint32_t st = MQ303_ST_DISABLED, Stamp = 0;
    
    if((!MQ303Enabled) && (st != MQ303_ST_DISABLED))
    {
        st = MQ303_ST_SHDN;
    }
    
    /* STATE MACHINE FOR MQ303
        during any cycle, if the MQ303Enable is FALSE, force SM to SHDN if it's not already disabled
    
        DISABLED    (start), when enabled pass to init                                  -> INIT
        INIT        start regulator and HV config for fast heat ramp                    -> HEAT_HV
        HEAT_HV     delay until heat ramp complete, LV config for maintenance heat      -> HEAT_LV
        HEAT_LV     delay until maintenance heat complete, disable supply for idle      -> IDLE
        IDLE        delay until maintenance necessary, LV config for maintenance heat   -> MAINT
        MAINT       delay until maintenance heat complete, disable supply for idle      -> IDLE
        SHDN        disable supply for disabled                                         -> DISABLED
    */
    
    switch(st)
    {
        case MQ303_ST_DISABLED:
            if(MQ303Enabled)
            {
                Stamp = GetTick();
                st = MQ303_ST_INIT;
            }
            break;
        case MQ303_ST_INIT:
            if(GetTick() - Stamp >= MQ303_WAIT_TO_INIT)
            {
                MQ303_HEATER_MAKE_2p2V;
                
                Stamp = GetTick();
                st = MQ303_ST_MAINT;
            }
            break;
        /* case MQ303_ST_HEAT_HV:
            if(GetTick() - Stamp >= MQ303_WAIT_TO_HEAT_HV)
            {
                MQ303_HEATER_MAKE_0p9V;
                
                Stamp = GetTick();
                st = MQ303_ST_HEAT_LV;
            }
            break;
        case MQ303_ST_HEAT_LV:
            if(GetTick() - Stamp >= MQ303_WAIT_TO_HEAT_LV)
            {
                MQ303_HEATER_DISABLE;
                
                Stamp = GetTick();
                st = MQ303_ST_IDLE;
            }
            break; */
        case MQ303_ST_IDLE:
            
            LatestReading = ConvADCToBACPPM(value);
            
            if(GetTick() - Stamp >= MQ303_WAIT_TO_IDLE)
            {
                if(!MQ303Enabled)
                {
                    Stamp = GetTick();
                    st = MQ303_ST_SHDN;
                }
            }
            break;
        case MQ303_ST_MAINT:
            
            LatestReading = ConvADCToBACPPM(value);
            
            if(GetTick() - Stamp >= MQ303_WAIT_TO_MAINT)
            {
                MQ303_HEATER_MAKE_0p9V;
                
                if(!MQ303Enabled)
                {
                    Stamp = GetTick();
                    st = MQ303_ST_SHDN;
                }
                else
                {
                    Stamp = GetTick();
                    st = MQ303_ST_IDLE;
                }
            }
            break;
        case MQ303_ST_SHDN:
        default:
            
            LatestReading = 0;
            
            MQ303_HEATER_DISABLE;
                
            st = MQ303_ST_DISABLED;
            break;
    }
    
    return st;
}

// module private functions
#define TABLE_WIDTH_ISO     5
#define TABLE_WIDTH_H       7
#define TABLE_WIDTH_ETH     7

const int32_t ISOButaneRatio[TABLE_WIDTH_ISO] = { 380, 270, 170, 120, 82 };
const int32_t ISOButanePPM[TABLE_WIDTH_ISO]   = { 100, 300, 1000, 3000, 10000 };

const int32_t HydrogenRatio[TABLE_WIDTH_H] = { 700, 500, 300, 210, 140, 90, 57 };
const int32_t HydrogenPPM[TABLE_WIDTH_H]   = { 10, 30, 100, 300, 1000, 3000, 10000 };

const int32_t EthanolRatio[TABLE_WIDTH_ETH] = { 610, 420, 260, 170, 100, 62, 38 };
const int32_t EthanolPPM[TABLE_WIDTH_ETH]   = { 10, 30, 100, 300, 1000, 3000, 10000 };

#if (MQ303_SELECTED_TYPE == MQ303_TYPE_ISO)
    #define TABLE_WIDTH     TABLE_WIDTH_ISO
    #define RATIO_PTR       (&ISOButaneRatio[0])
    #define PPM_PTR         (&ISOButanePPM[0])
#elif (MQ303_SELECTED_TYPE == MQ303_TYPE_H)
    #define TABLE_WIDTH     TABLE_WIDTH_H
    #define RATIO_PTR       (&HydrogenRatio[0])
    #define PPM_PTR         (&HydrogenPPM[0])
#elif (MQ303_SELECTED_TYPE == MQ303_TYPE_ETH)
    #define TABLE_WIDTH     TABLE_WIDTH_ETH
    #define RATIO_PTR       (&EthanolRatio[0])
    #define PPM_PTR         (&EthanolPPM[0])
#endif

uint32_t ConvADCToBACPPM( uint32_t adc )
{
    int32_t LookUpValue = (MQ303_ADC_CH_PU_VAL * adc) / (3300 - adc);
    
    int32_t PPMEstimate = 0, i, RatioPercent;
    const int32_t wTable = TABLE_WIDTH;
    const int32_t* const aRatio = RATIO_PTR;
    const int32_t* const aPPM = PPM_PTR;
    
    #define ISBETWEEN(x, a, b)      (((x >= a) && (x <= b)) || ((x >= b) && (x <= a)))
    
    if(ISBETWEEN(LookUpValue, aRatio[0], aRatio[wTable - 1]))
    {
        for(i = 0; i < wTable - 1; i++)
        {
            if(ISBETWEEN(LookUpValue, aRatio[0], aRatio[1]))
            {
                RatioPercent  = (100 * (LookUpValue - aRatio[0])) / (aRatio[1] - aRatio[0]);
                PPMEstimate = RatioPercent * (aPPM[0] - aPPM[1]);
                
                break;
            }
        }
    }
    else
    {
        if(aRatio[0] < aRatio[wTable - 1])
        {
            PPMEstimate = ((LookUpValue < aRatio[0]) ? aRatio[0] : aRatio[wTable - 1]);
        }
        else
        {
            PPMEstimate = ((LookUpValue < aRatio[wTable - 1]) ? aRatio[wTable - 1] : aRatio[0]);
        }
    }
    
    return PPMEstimate;
}

// module functions
uint32_t GetMQ303( void )
{
    
    return LatestReading;
}

void EnableMQ303( void )
{
    MQ303Enabled = TRUE;
}

void DisableMQ303( void )
{
    MQ303Enabled = FALSE;
}

/* [] END OF FILE */
